#include "Framework.h"
#include "..\utils\Log.h"
#include "..\dxstdafx.h"
#include "..\resource.h"
#include "Engine.h"
#include "..\content\ContentPipeline.h"

const double dt = 0.01;

namespace ds {

CFramework *m_Framework;

CFramework::CFramework(CGame* baseApp) : m_Game(baseApp) {
	m_Framework = this;
	totalTime = 0.0;
	accumulator = dt;
	currentTime = GetTickCount();
	g_dLastTime = GetTickCount();
	gEngine = new Engine();
}

void CFramework::initialize(HINSTANCE hInstance,const Settings& settings) {	
	m_active = true; 
	m_hInstance = hInstance;
    m_windowWidth = settings.width;
    m_windowHeight = settings.height;
	
	RECT DesktopSize;

	GetClientRect(GetDesktopWindow(),&DesktopSize);
    // Register the window class
	WNDCLASSEX wcex;
   wcex.cbSize         = sizeof( WNDCLASSEX );
   wcex.style          = CS_DBLCLKS;
   wcex.lpfnWndProc    = (WNDPROC)CFramework::StaticWndProc;
   wcex.cbClsExtra     = 0;
   wcex.cbWndExtra     = 0;
   wcex.hInstance      = hInstance;
   wcex.hIcon          = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_CUNIT ) );
   wcex.hCursor        = LoadCursor( NULL, IDC_ARROW );
   wcex.hbrBackground  = (HBRUSH)GetStockObject( BLACK_BRUSH );
   wcex.lpszMenuName   = NULL;
   wcex.lpszClassName  = settings.title;
   wcex.hIconSm        = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_CUNIT ) ); 


    WNDCLASSEX wc =
    {
		sizeof( WNDCLASSEX ), CS_CLASSDC, (WNDPROC)CFramework::StaticWndProc, 0L, 0L,
        GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
        "Diesel", NULL
    };
    RegisterClassEx( &wc );

    // Create the application's window
    m_hWnd = CreateWindow( "Diesel", "Diesel",
                            WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
							(DesktopSize.right - settings.width) / 2,(DesktopSize.bottom - settings.height) / 2,
							settings.width, settings.height,
                            NULL, NULL, wc.hInstance, NULL );
	
	if (!m_hWnd) 	{
		MessageBoxA(NULL, "Error creating program window!", "Error", MB_OK);
	}

    // Adjust to desired size
    RECT rect = { 0, 0, settings.width, settings.height };
    AdjustWindowRect( &rect, GetWindowLong( m_hWnd, GWL_STYLE ), FALSE );
    SetWindowPos( m_hWnd, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, 
        SWP_NOZORDER | SWP_NOMOVE  );

    ShowWindow( m_hWnd, SW_SHOW );
    UpdateWindow( m_hWnd );

	SetWindowText(m_hWnd,settings.title);

    // Save current location/size
    ZeroMemory( &m_wp, sizeof( WINDOWPLACEMENT ) );
    m_wp.length = sizeof( WINDOWPLACEMENT );
    GetWindowPlacement( m_hWnd, &m_wp );	
	
	m_Settings = settings;

	LOGC(logINFO,"Framework") << "window created";
	// create the global engine
	gEngine->init(m_hWnd,m_Settings,Rect(m_wp.rcNormalPosition));

	LOGC(logINFO,"Framework") << "loading content";
	// try to do some auto content loading now
	//gEngine->getContentLoader().processFiles("data\\resources");
	// load textures and sounds
	//gEngine->getResourceManager().loadTextures("content\\textures");
	//gEngine->getResourceManager().loadSounds("content\\sounds");
	/*
	ContentPipeline cp;
#ifdef DEBUG
	cp.scanDirectory("content\\resources");
	cp.processFiles();
	cp.savePakFile("content\\data.pak");
#else
	cp.loadPakFile("content\\data.pak");
#endif
	cp.createResources(gEngine->getResourceManager());
	*/
	if ( m_Settings.readPlainResources ) {
		LOGC(logINFO,"Framework") << "reading plain resources";
		gEngine->getDeclarationManager().processDirectory("content");
		gEngine->getDeclarationManager().createResources();
	}
	else {
		LOGC(logINFO,"Framework") << "reading content.pak file";
	}
	// we are about to initialize everything now
	LOGC(logINFO,"Framework") << "initializing game";
	// start with the game
	m_Game->initialize();
	//LOGC(logINFO,"Framework") << "calling load content in Game";
	//m_Game->loadContent(gEngine->getResourceManager());	

	
	// now initialize the game states and call loadContent
	LOGC(logINFO,"Framework") << "initializing game states";
	gEngine->initializeStates();

	LOGC(logINFO,"Framework") << "initializing engine components and services";
	gEngine->initializeComponents();	

	// now initialize all nodes and then call loadContent
	LOGC(logINFO,"Framework") << "initializing all nodes";
	gEngine->initializeNodes();

	LOGC(logINFO,"Framework") << "calling load content in application";
	//m_GameApp->loadContent(gEngine->resourceManager);
	LOGC(logINFO,"Framework") << "preparing application";
	//m_GameApp->OnCreateDevice();
	//m_GameApp->OnResetDevice();

	LOGC(logINFO,"Framework") << "calling load content in Game";
	m_Game->loadContent(gEngine->getResourceManager());	

	gEngine->finalizeInitialisation();		
}

CFramework::~CFramework() { 
	delete gEngine;	
	delete m_Game;
}

// ------------------------------
// This is the actual clean up
// ------------------------------
void CFramework::Release() {
	LOGC(logINFO,"Framework") << "CFramework -> dest called";	
    OnLostDevice();
	OnDestroyDevice();
    Shutdown();
	LOGC(logINFO,"Framework") << "CFramework all methods called deleting objects";	
	
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Runs the application
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CFramework::Run() {	
    MSG msg;
    while ( m_active ) {
        // Did we receive a message, or are we idling ?
        if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )  {
            if ( msg.message == WM_QUIT) {
                break;
            }
            TranslateMessage( &msg );
            DispatchMessage ( &msg );
        } 
        else  {
            if ( m_active ) {				
				double newTime = GetTickCount();
				double frameTime = newTime - currentTime;
				if ( frameTime > 0.25 ) {
					  frameTime = 0.25;	  // note: max frame time to avoid spiral of death
				}
				currentTime = newTime;
				/*
				accumulator += frameTime;
				
				 while ( accumulator >= dt )  {
					  // update here dt
					  //integrate( currentState, t, dt );
					 LOG(logINFO) << "updating system now";
					 totalTime += dt;
					  accumulator -= dt;
				 }

				 const double alpha = accumulator / dt;
				*/
				g_dCurTime     = GetTickCount();
				g_fElapsedTime = (float)((g_dCurTime - g_dLastTime) * 0.001);
				g_dLastTime    = g_dCurTime;
				gEngine->buildFrame(g_fElapsedTime);
            }
        } 
    }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Called after the device is created. Create D3DPOOL_MANAGED resources here.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CFramework::OnCreateDevice() {
	LOGC(logINFO,"Framework") << "CFramework OnCreateDevice";
	/*
    if ( m_GameApp != NULL ) {
        m_GameApp->OnCreateDevice();
    }
	*/
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Called after the device is reset. Create D3DPOOL_DEFAULT resources here.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CFramework::OnResetDevice() {
	LOGC(logINFO,"Framework") << "CFramework OnResetDevice";
	/*
    if ( m_GameApp != NULL ) {
        m_GameApp->OnResetDevice();
    }
	*/
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Called when the device is lost. Release D3DPOOL_DEFAULT resources here.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CFramework::OnLostDevice() {
	LOGC(logINFO,"Framework") << "CFramework OnLostDevice";
	/*
    if ( m_GameApp != NULL ) {
        m_GameApp->OnLostDevice();
    }
	*/
}

void CFramework::OnDestroyDevice() {
	LOGC(logINFO,"Framework") << "CFramework OnDestroyDevice";
	/*
    if ( m_GameApp != NULL ) {
        m_GameApp->OnDestroyDevice();
    }
	*/
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Called after the device is destroyed. Release D3DPOOL_MANAGED resources here.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CFramework::Shutdown() {
	LOGC(logINFO,"Framework") << "CFramework Shutdown";
    if ( m_Game != NULL && m_active ) {
		m_active = false;
    }	
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Toggles between fullscreen and windowed mode.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CFramework::ToggleFullscreen()
{
    if ( !m_active ) {
        return;
    }

    gEngine->getRenderer()->Windowed = !gEngine->getRenderer()->Windowed;

    // Set new window style
    if ( gEngine->getRenderer()->Windowed )
    {
        // Going to windowed mode
        SetWindowLongPtr( m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW );
    }
    else 
    {
        // Save current location/size
        ZeroMemory( &m_wp, sizeof( WINDOWPLACEMENT ) );
        m_wp.length = sizeof( WINDOWPLACEMENT );
        GetWindowPlacement( m_hWnd, &m_wp );

        // Going to fullscreen mode
        SetWindowLongPtr( m_hWnd, GWL_STYLE, WS_EX_TOPMOST );

        // Hide the window to avoid animation of blank windows
        ShowWindow( m_hWnd, SW_HIDE );
    }

    // Reset the Device
    OnLostDevice();
    gEngine->getRenderer()->Reset();
    OnResetDevice();

    if ( gEngine->getRenderer()->Windowed )
    {
        // Going to windowed mode
        // Restore the window location/size
        SetWindowPlacement( m_hWnd, &m_wp );
    }

    // Make the window visible
    if ( !IsWindowVisible( m_hWnd ) )
    {
        ShowWindow( m_hWnd, SW_SHOW );
    }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Event handler. Routes messages to appropriate instance.
Parameters:
[in] hWnd - Unique handle to the window.
[in] message - Incoming message.
[in] wParam - Parameter of the message (unsigned int).
[in] lParam - Parameter of the message (long).
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
LRESULT CALLBACK CFramework::StaticWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) {
	return m_Framework->WndProc(hWnd,msg,wParam,lParam);    
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Application event handler.
Parameters:
[in] hWnd - Unique handle to the window.
[in] message - Incoming message.
[in] wParam - Parameter of the message (unsigned int).
[in] lParam - Parameter of the message (long).
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
LRESULT CALLBACK CFramework::WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) {

    switch ( message ) {

		case WM_DESTROY:
			DestroyWindow( hWnd );
			PostQuitMessage( 0 );
			return 0;
		case WM_CLOSE:
			DestroyWindow( hWnd );
			PostQuitMessage( 0 );
			return 0;		
		case WM_SIZE:
			if ( wParam == SIZE_MINIMIZED ) {
				// Disable application on minimized
				m_active = false;
			} 
			return 0;
		case WM_CHAR: 
			{
				char ascii = wParam;
				unsigned int keyState = lParam;
				gEngine->OnChar(ascii,keyState);
			}
			return 0;
		case WM_KEYDOWN:        
			gEngine->OnKeyDown( wParam );
			return 0;
		case WM_KEYUP:        
			gEngine->OnKeyUp( wParam );
			return 0;
		case WM_LBUTTONDOWN:		
			gEngine->onButtonDown(0);
			return 0;
		case WM_LBUTTONUP:		
			gEngine->onButtonUp(0);
			return 0;
		case WM_RBUTTONDOWN:		
			gEngine->onButtonDown(1);
			return 0;
		case WM_RBUTTONUP:		
			gEngine->onButtonUp(1);
			return 0;
		case WM_MOUSEMOVE:		
			gEngine->setMousePosition(MAKEPOINTS(lParam));
			return 0;
    }
    return DefWindowProc( hWnd, message, wParam, lParam );
}

};